#了解了迭代器，那么生成器是什么？与迭代器有何关系

"""
    生成器(Generator)是Python中创建迭代器的简洁方式，主要通过两种语法实现：生成器函数和生成器表达式。
"""

"""
基本语法：
    ·使用def定义普通函数
    ·函数体内必须包含至少一个yield语句
    ·调用时返回生成器对象(不会立刻执行函数体)

        def 生成器函数名(参数):
        # 函数体
        yield 值  # 可以有多处yield
        # 更多代码...
    yield关键字特性
        当函数执行到yield时：
            ·返回yield后面的值
            ·暂停函数执行并保存所有局部变量状态
            ·下次调用next()时从暂停处继续执行
        ·一个函数可以有多个yield语句
        ·执行完所有代码或遇到return时抛出StopIteration
"""

#简单示例
def simple_gen():
    print('开始执行')
    yield 1
    print('继续执行')
    yield 2
    print('结束执行')

#使用生成器
gen = simple_gen() #创建生成器对象，不执行函数体
print(next(gen))
print(gen.__next__())


"""
生成器表达式语法：
    ·基本结构，类似于列表推导式，但是也圆括号：
        (表达式 for 变量 in 可迭代对象 [if 条件])
"""

#使用实例
squares_gun = (x ** 2 for x in range(5))

for square in squares_gun:
    print(square, end=' ')
print()

#带条件的生成器表达式
even_squares = (x ** 2 for x in range(10) if x % 2 == 0)
for square in even_squares:
    print(square, end=' ')
print()

"""
生成器对象支持的方法(除支持next()外)：
    ·send(value)：
        ·向生成器发送值（该值会成为当前yield表达式的结果）
        ·首次调用必须发送None（或先调用next()）
    ·throw(type[, value[, traceback]])
    ·close()

"""
def responsive_gen():
    print('准备接收')
    x = yield '请发送值：'
    yield f'收到：{x}'
#send 方法实现双向交互的基础就是 yield 的暂停执行和保存状态特性，
#借助 send 方法可以向暂停的生成器发送值，改变生成器内部的变量状态，从而实现更灵活的交互
gen = responsive_gen()
print(next(gen))
print(gen.send('你好啊！'))
#为什么首次调用send，需要传入none：
#因为此时生成器还未暂停在 yield 语句处，没有 yield 表达式来接收值；或者先调用 next() 
#让生成器执行到第一个 yield 语句处暂停。

#在生成器暂停处抛出指定异常
def exception_gen():
    try:
        yield '正常执行'
    except ValueError:
        yield '捕获异常'

gen = exception_gen()
print(next(gen))
print(gen.throw(ValueError))

# 终止生成器（在暂停处抛出GeneratorExit异常）
def closable_gen():
    try:
        yield '运行中'
    except GeneratorExit:
        print('清理资源')

gen = closable_gen()
print(next(gen))
gen.close()

#yield from语法（Python 3.3+）
# yield from 可迭代对象(或生成器)

# 主要用途
# 委托给子生成器
# 简化嵌套生成器的代码

def sub_gen():
    yield print(1)
    yield print(2)

def main_gen():
    yield print('开始')
    yield from sub_gen()
    yield print('结束')
gen = main_gen()
list(main_gen())


